package cn.bb.testDemo;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;

public class RpcFramework {

    /**
     * 暴露服务
     * 也就是服务的生产者
     * @param service 服务实现
     * @param port 服务端口
     * @throws Exception
     */
    public static void export(final Object service, int port) throws Exception {

        System.out.println("Export service :" + service.getClass().getName() + "||||| on port " + port);
        //启动socket服务
        ServerSocket server = new ServerSocket(port);
        //死循环 相当于  while（true）
        for(;;) {
            try {
                //00
                final Socket socket = server.accept();

                new Thread(new Runnable() {  //匿名类  写法  只用这一次  所以匿名  不用起名字了
                    @Override
                    public void run() {
                        try {
                            try {
                                //ObjectInputStream  为 getInputStream的包装类  提供更多的方法  如读取字符串  整数  小数  对象
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                try {
                                    //获得方法名 RPC固定的协议
                                    String methodName = input.readUTF();
                                    //泛型的class类的数组   参数类型  多态的体现
                                    Class<?>[] parameterTypes = (Class<?>[])input.readObject();  //多了很多read方法

                                    //参数值
                                    Object[] arg = (Object[])input.readObject();

                                    //OutputStream  包装类
                                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                                    try {
                                        //反射  核心思想
                                        //传入 方法名和  参数类型  精确找到方法  jdk自己的方法
                                        Method method = service.getClass().getMethod(methodName, parameterTypes);
                                        Object result = method.invoke(service, arg);
                                        output.writeObject(result);

                                    } catch (Throwable t) {
                                        output.writeObject(t);
                                    } finally {
                                        output.close();
                                    }
                                } finally {
                                    input.close();
                                }
                            } finally {
                                socket.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } catch (Exception e) {
                e.printStackTrace();
                server.close();
            }
        }
    }

    /**
     * 引用服务
     * 也就是服务的消费者
     *  ------jdk的动态代理  必须是接口 多实现 单继承
     * @param <T> 接口泛型
     * @param interfaceClass 接口泛型类对象
     * @param host 服务器主机名
     * @param port 服务器端口
     * @return 远程服务  <T>泛型实例对象
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
        if (interfaceClass == null)
            throw new IllegalArgumentException("Interface class == null");
        //jdk的动态代理  必须是接口 多实现 单继承
        if (! interfaceClass.isInterface())
            throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
        if (host == null || host.length() == 0)
            throw new IllegalArgumentException("Host == null!");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        //上面4个if就是为了代码的健壮性
        System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);


        //核心  return 一个实例化对象
        //参数 1  类加载器     2 接口数组   3覆盖重写接口类
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass},
                new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {

                    Socket socket = new Socket(host, port);
                    try {
                        //对象流 包装下
                        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                        try {
                            output.writeUTF(method.getName()); //方法名
                            output.writeObject(method.getParameterTypes());//方法的参数类型数组
                            output.writeObject(arguments);  //参数值
                            //接受服务器端的返回值
                            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());

                            try {
                                Object result = input.readObject(); //服务器端写回来的就是object
                                //如果是异常类型的 往上层抛
                                if (result instanceof Throwable) {
                                    throw (Throwable) result;
                                }
                                //无异常 就正常执行
                                return result;  //返回最后的结果result
                            } finally {
                                input.close();
                            }
                        } finally {
                            output.close();
                        }
                    } finally {
                        socket.close();
                    }
                }
                });
    }

}

